package cn.cutie.clotrpc.core.registry.clot;

import cn.cutie.clotrpc.core.api.RegistryCenter;
import cn.cutie.clotrpc.core.consumer.HttpInvoker;
import cn.cutie.clotrpc.core.meta.InstanceMeta;
import cn.cutie.clotrpc.core.meta.ServiceMeta;
import cn.cutie.clotrpc.core.registry.ChangedListener;
import cn.cutie.clotrpc.core.registry.Event;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @Description: 实现clot注册中心注册
 * @Author: Cutie
 * @CreateDate: 2024/4/24 20:21
 * @Version: 0.0.1
 */
@Slf4j
public class ClotRegistryCenter implements RegistryCenter {

    private static final String REG_PATH = "/reg";
    private static final String UNREG_PATH = "/unreg";
    private static final String FIND_ALL_PATH = "/findAll";
    private static final String VERSION_PATH = "/version";
    private static final String RENEWS_PATH = "/renews";

    @Value("${clotregistry.servers}")
    private String servers;

    Map<String, Long> VERSIONS = new HashMap<>();
    MultiValueMap<InstanceMeta, ServiceMeta> RENEWS = new LinkedMultiValueMap<>();

    ClotHealthChecker clotHealthChecker = new ClotHealthChecker();

    @Override
    public void start() {
        log.info(" ===> [ClotRegistry] start with servers:{}", servers);
        clotHealthChecker.start();
        providerCheck();
    }

    private void providerCheck(){
        clotHealthChecker.providerChecker(()->{
            RENEWS.keySet().stream().forEach(
                    instance -> {
                        Long timestamp = HttpInvoker.httpPost(JSON.toJSONString(instance), renewsPath(RENEWS.get(instance)), Long.class);
                        log.info(" ===> [ClotRegistry] renew instance {} at {}", instance, timestamp);
                    }
            );
        });
    }

    @Override
    public void stop() {
        log.info(" ===> [ClotRegistry] stop with servers:{}", servers);
        clotHealthChecker.stop();
    }

    @Override
    public void register(ServiceMeta service, InstanceMeta instance) {
        log.info(" ===> [ClotRegistry] register instance {} for {}", instance, service);
        HttpInvoker.httpPost(JSON.toJSONString(instance), regPath(service), InstanceMeta.class);
        log.info(" ===> [ClotRegistry] register instance {} registered", instance);
        RENEWS.add(instance, service);
    }

    @Override
    public void unRegister(ServiceMeta service, InstanceMeta instance) {
        log.info(" ===> [ClotRegistry] unRegister instance {} for {}", instance, service);
        HttpInvoker.httpPost(JSON.toJSONString(instance), unregPath(service), InstanceMeta.class);
        log.info(" ===> [ClotRegistry] register instance {} unregistered", instance);
        RENEWS.remove(instance, service);
    }

    @Override
    public List<InstanceMeta> fetchAll(ServiceMeta service) {
        log.info(" ===> [ClotRegistry] fetchAll instance for {}", service);
        List<InstanceMeta> instances = HttpInvoker.httpGet(findAllPath(service), new TypeReference<List<InstanceMeta>>(){});
        log.info(" ===> [ClotRegistry] fetchAll instance {}", instances);
        return instances;
    }

    // listener里面相当于放了个壳子，里面的fire方法调用到的时候就进行回调方法的执行
    @Override
    public void subscribe(ServiceMeta service, ChangedListener listener) {
        clotHealthChecker.consumerChecker(()->{
            Long version = VERSIONS.getOrDefault(service.toPath(), -1L);
            Long newVersion = HttpInvoker.httpGet(versionPath(service), Long.class);
            log.info(" ===> [ClotRegistry] subscribe instance for {}, version:{} , newVersion:{}", service, version, newVersion);
            // 注册中心版本大于当前版本说明版本更新了
            if (newVersion > version){
                List<InstanceMeta> instances = this.fetchAll(service);
                listener.fire(new Event(instances));
                VERSIONS.put(service.toPath(), newVersion);
            }
        });
    }

    private String regPath(ServiceMeta service) {
        return path(REG_PATH, service);
    }
    private String unregPath(ServiceMeta service) {
        return path(UNREG_PATH, service);
    }
    private String findAllPath(ServiceMeta service) {
        return path(FIND_ALL_PATH, service);
    }
    private String versionPath(ServiceMeta service) {
        return path(VERSION_PATH, service);
    }

    private String path(String context, ServiceMeta service){
        return servers + context + "?service=" + service.toPath();
    }

    private String renewsPath(List<ServiceMeta> serviceList){
       return path(RENEWS_PATH, serviceList);
    }

    private String path(String context, List<ServiceMeta> serviceList){
        StringBuffer sb = new StringBuffer();
        for (ServiceMeta service : serviceList) {
            sb.append(service.toPath()).append(",");
        }
        String services = sb.toString();
        if(services.endsWith(",")) services = services.substring(0, services.length() - 1);
        log.info(" ===> [ClotRegistry] path services :{}", services);
        return servers + context + "?services=" + services;
    }
}
